package com.AdvancedDemos.LearnThread多线程;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class 线程创建方式三 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 前两种线程的创建方式都存在一个问题
         * 假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果
         *
         * JDK5.0提供了Callable接口和FutureTask类来实现--线程的第三种创建方式
         * 这种方式最大的优点是:可以返回线程执行完毕后的结果
         */

        /**
         * 多线程的第三种创建方式:利用Callable接口、FutureTask类来实现。
         * ① 创建任务对象
         *      定义一个类实现Callable接口，重写call方法，封装要做的事情，和要返回的数据。
         *      把Callable类型的对象封装成FutureTask（线程任务对象)。
         * ② 把线程任务对象交给Thread对象。
         * ③ 调用Thread对象的start方法启动线程。
         * ④ 线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
         */

        /**
         * 线程创建方式三的优缺点:
         * 优点∶线程任务类只是实现接口，可以继续继承类和实现接口，扩展性强;可以在线程执行完毕后去获取线程执行的结果。
         * 缺点:编码复杂一点。
         */


        /*
        3.创建一个MyCallable对象
         */
        Callable<String> myCallable = new MyCallable(100);

        /*
        4.Callable本身并不是线程任务对象,
        把Callable对象封装成FutureTask对象(任务对象)
        FutureTask的API
        public FutureTask<>(Callable call) --- 把Callable对象封装成FutureTask对象
        public V get( ) throws Exception --- 获取线程执行call方法返回的结果
         */
        FutureTask<String> futureTask = new FutureTask<>(myCallable);//未来任务对象的作用:1.是一个任务对象,实现了Runnable接口; 2.可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕之后的结果

        /*
        5.把任务对象交给一个Thread对象
         */
        new Thread(futureTask).start();

        Callable<String> myCallable1 = new MyCallable(200);
        FutureTask<String> futureTask1 = new FutureTask<>(myCallable1);
        new Thread(futureTask1).start();
        /**
         * 注意:用同一个FutureTask线程任务对象再次新建线程,将无法运行,想要重启一个Callable对象中的任务,必须重新创建FutureTask线程任务对象,放入Thread构造中
         */

        /*
        6.获取线程执行之后的返回结果
         */
        String result = futureTask.get();//注意:假如执行到这,上面的线程还没有执行完毕,代码会暂停,等待上面线程执行完毕后才会获取结果
        System.out.println(result);//线程求出了1-100的和是:5050

        String result1 = futureTask1.get();//如果第二个线程没有跑完,会暂停等待
        System.out.println(result1);//线程求出了1-200的和是:20100


    }
}

/*
1.自定义类实现Callable接口
 */
class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    /*
        2.重写call方法
         */
    @Override
    public String call() throws Exception {
        //描述线程的任务,返回线程执行完毕的返回值
        //求1-n的和并返回
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "线程求出了1-" + n + "的和是:" + sum;
    }
}
